package cn.nawang.ebeim.client.transfer;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.nawang.ebeim.client.bean.ClientConfiguration;
import cn.nawang.ebeim.client.util.DownloadProcessBarList;
import cn.nawang.ebeim.client.util.FileUtil;
import cn.nawang.ebeim.common.entity.FileInfo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

public class HttpDownloadClientHandler extends SimpleChannelInboundHandler<HttpObject> {
  private static final Logger LOG = LoggerFactory.getLogger(HttpDownloadClientHandler.class);
  public long fileLength;// 服务端传来文件的总字节
  public long count;// 累计服务端传来文件的字节
  private File file;// 临时文件名
  private RandomAccessFile randomFile;
  private FileInfo fileInfo;
  private static final int MAX_PONG_TIMES = 10; // 定义客户端没有收到服务端的pong消息的最大次数
  private int pongTimes = 0;
  private boolean bool;
  private String md5;
  private boolean connect;

  public boolean isConnect() {
    return connect;
  }

  public void setConnect(boolean connect) {
    this.connect = connect;
  }

  public boolean isBool() {
    return bool;
  }

  public void setBool(boolean bool) {
    this.bool = bool;
  }

  public HttpDownloadClientHandler(FileInfo fileInfo) {
    this.fileInfo = fileInfo;
  }

  @Override
  public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    if (evt instanceof IdleStateEvent) {
      IdleStateEvent event = (IdleStateEvent) evt;
      if (event.state() == IdleState.READER_IDLE) {
        LOG.info("===客户端===(WRITER_IDLE 读超时)" + pongTimes);
      } else if (event.state() == IdleState.WRITER_IDLE) {
        if (pongTimes >= MAX_PONG_TIMES) {
          LOG.info("===客户端===(WRITER_IDLE 写超时)" + pongTimes);
          if (randomFile != null) {
            randomFile.close();
          }
          bool=false;
          ctx.channel().close();
        } else {
          // 失败计数器加1
          pongTimes++;
        }
      } else if (event.state() == IdleState.ALL_IDLE) {
        /* 总超时 */
        LOG.info("===客户端===(ALL_IDLE 总超时)");
      }
    }
  }

  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    connect = true;
  }

  @Override
  public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
    pongTimes = 0;
    if (msg instanceof HttpResponse) {

      HttpResponse response = (HttpResponse) msg;
      LOG.info("STATUS: " + response.getStatus());
      if (response.getStatus().code() == 200) {
        // 生成临时文件
        file = getFile(fileInfo);
        LOG.info("开始下载:" + file.getAbsolutePath());
        md5 = response.headers().get("md5");
        fileLength = response.headers().getContentLength(response);
        if (fileLength == 0 && file.exists()) {
          // 验证文件的正确性
          bool = validateFile(file);
          if (!bool) {
            file.delete();
          } else {
            // 验证文件的重命名
            bool = reNameFile(file);
          }
          ctx.close();
        } else {
          randomFile = new RandomAccessFile(file, "rw");
          long downLength = randomFile.length();
          randomFile.seek(downLength);
        }
      }else{
        LOG.info("error: " + response.headers().get("error"));
        bool=false;
      }
    }
    if (msg instanceof HttpContent && fileLength != 0) {
      HttpContent chunk = (HttpContent) msg;
      ByteBuf buf = chunk.content();
      count += buf.readableBytes();
      if (fileInfo.getNid() != null) {
        DownloadProcessBarList.summationTotal(fileInfo.getNid(), buf.readableBytes());
      }
      double rate = count * 100 / fileLength;
      LOG.debug("下载进度：" + rate);
      byte[] req = new byte[buf.readableBytes()];
      buf.readBytes(req);
      // 开始写入文件内容
      randomFile.write(req);
      if (chunk instanceof LastHttpContent) {
        randomFile.close();
        // 验证文件正确性
        bool = validateFile(file);
        if (!bool) {
          file.delete();
          LOG.info(file.getAbsolutePath() + "---删除！");
        } else {
          // 验证文件的重命名
          bool = reNameFile(file);
        }
        ctx.close();
      }
    }
  }


  public boolean reNameFile(File file) {
    File lastFile = null;
    lastFile = getLastFile(fileInfo);
    boolean boolFile = false;
    try {
      if (lastFile.exists()) // 若在该目录下已经有一个文件和新文件名相同，则不允许重命名
        LOG.info(lastFile.getPath() + "已经存在！");
      else {
        boolFile = file.renameTo(lastFile);
      }
      if (boolFile) {
        LOG.info("客户端已成功下载文件，保存路径：" + lastFile.getPath());
      } else {
        LOG.info("重命名失败，文件名：" + file);
      }
    } catch (Exception e) {
      LOG.info(e.getMessage());
      e.printStackTrace();
    }
    return boolFile;
  }

  public boolean validateFile(File file) {
    FileInputStream in;
    String newMd5 = null;
    try {
      in = new FileInputStream(file);
      newMd5 = DigestUtils.md5Hex(in);
      in.close();
    } catch (Exception e) {
      LOG.info(e.getMessage());
      e.printStackTrace();
    }
    if (!md5.equals(newMd5)) {
      LOG.info("文件验证不通过");
      return false;
    } else {
      LOG.info("文件验证通过");
      return true;
    }
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    LOG.info("异常");
    try {
      if (randomFile != null) {
        randomFile.close();
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    cause.printStackTrace();
    ctx.channel().close();
  }

  /**
   * 生成临时文件名
   * 
   * @author: huanghj
   * @Description: 创建
   * @param @param info
   * @param @return
   * @param @throws Exception
   * @return File
   * @date 2015年9月17日下午2:24:07
   *
   * @修改人
   * @修改日期
   */
  public static File getFile(FileInfo info) {
    String fileName = null;
    File dir = null;
    byte type = info.getType();
    if (type == FileInfo.TYPE_FILE_DOWNLOAD) {
      fileName = info.getSignature();
      dir = new File(FileUtil.getDownloadDataFilePath(info.getDataId()));
    } else if (type == FileInfo.TYPE_VERSION_DOWNLOAD) {
      fileName = info.getVersion();
      dir = new File(FileUtil.getZipVersionFilePath(info.getDataId(), ""));
    }
    if (!dir.getParentFile().exists()) {
      dir.getParentFile().mkdirs();
    }
    // 生成临时文件名
    File file = new File(dir + File.separator + ClientConfiguration.getTempFilePrefix() + fileName);
    if (!file.getParentFile().exists()) {
      file.getParentFile().mkdir();
    }
    return file;
  }

  /**
   * 生成最终文件名
   * 
   * @author: huanghj
   * @Description: TODO
   * @param @param info
   * @param @return
   * @param @throws Exception
   * @return File
   * @date 2015年9月21日下午6:03:30
   *
   * @修改人
   * @修改日期
   */
  public File getLastFile(FileInfo info) {
    String fileName = null;
    File dir = null;
    byte type = info.getType();
    if (type == FileInfo.TYPE_FILE_DOWNLOAD) {
      fileName = info.getSignature();
      dir = new File(FileUtil.getDownloadDataFilePath(info.getDataId()));
    } else if (type == FileInfo.TYPE_VERSION_DOWNLOAD) {
      fileName = info.getVersion() + ".zip";
      dir = new File(FileUtil.getZipVersionFilePath(info.getDataId(), ""));
    }
    if (!dir.getParentFile().exists()) {
      dir.getParentFile().mkdirs();
    }
    // 最终生成的文件
    File lastFile = new File(dir + File.separator + fileName);
    return lastFile;
  }
}
